package gov.dhs.cis.soa;

import gov.dhs.cis.soa.Constants.DataDirType;
import gov.dhs.cis.soa.Constants.EntityType;
import gov.dhs.cis.soa.data.Actor;
import gov.dhs.cis.soa.data.Agent;
import gov.dhs.cis.soa.data.Application;
import gov.dhs.cis.soa.data.BusinessProcess;
import gov.dhs.cis.soa.data.DataClass;
import gov.dhs.cis.soa.data.EntitiesReferences;
import gov.dhs.cis.soa.data.InfrastructureLS;
import gov.dhs.cis.soa.data.InfrastructureLSInst;
import gov.dhs.cis.soa.data.InfrastructurePS;
import gov.dhs.cis.soa.data.IntegrationComp;
import gov.dhs.cis.soa.data.IntegrationSW;
import gov.dhs.cis.soa.data.Interface;
import gov.dhs.cis.soa.data.Processes;
import gov.dhs.cis.soa.data.RelatedDataReference;
import gov.dhs.cis.soa.data.RelatedProperties;
import gov.dhs.cis.soa.data.SecurityComp;
import gov.dhs.cis.soa.data.Service;
import gov.dhs.cis.soa.data.ServiceOperation;
import gov.dhs.cis.soa.data.UseCase;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import org.apache.log4j.Logger;


public class SOAData {
	static final Logger loger = Logger.getLogger(SOAData.class);

	public List<Application> appList = null;
	public List<SecurityComp> secCompList = null;
	public List<IntegrationSW> intSWList = null;
	public List<IntegrationComp> intCompList = null;
	public List<InfrastructureLS> infLSList = null;
	public List<InfrastructurePS> infPSList = null;
	public List<InfrastructureLSInst> infLSInstList = null;
	
	public List<Processes> procList = null;
	public List<DataClass> dataList = null;
	public List<Service> servList = null;
	public List<Interface> intList = null;
	public List<Actor> actorList = null;
	public List<UseCase> useCaseList = null;
	public List<Agent> agentList = null;
	
	public List<RelatedDataReference> dataRefList = null;
	public List<RelatedProperties> propsList = null;
	//public List<RelatedProcEntities> relProcList = null;
	public List<EntitiesReferences> entityRef = null;

	public ArrayList<BusinessProcess> bProcessList = null;
	
	private List<RelatedDataReference> appendRelatedDataReference2GivenList(List<RelatedDataReference> accumRefList, List<RelatedDataReference> fromList) {
		if(fromList != null && fromList.size() > 0) {
			List<RelatedDataReference> tList = null;
			for (RelatedDataReference iRf : fromList) {
				tList = Utils.findRelatedDataEntities(accumRefList, iRf);
				if(tList == null || tList.size() == 0) {
					if(accumRefList == null)
						accumRefList = new ArrayList<RelatedDataReference>();
					accumRefList.add(iRf);
				}
			}
		}
		return accumRefList;
	}

	public void addRelatedDataReference(List<RelatedDataReference> list) {
		dataRefList = appendRelatedDataReference2GivenList(dataRefList, list);
	}
	
	public void addEntitiesReferences(EntitiesReferences entRef) {
		if(entRef != null) {
			List <EntitiesReferences> tList = Utils.findEntitiesReferences(entityRef, entRef);
			if(tList == null || tList.size() == 0) {
				if(entityRef == null)
					entityRef = new ArrayList<EntitiesReferences>();
				entityRef.add(entRef);
			}
		}
	}
	
	public void resolveAllDependices() {
		resolveApplicationDependices();
		resolveIntegrationUCDependices();
		resolveInfrastructureDependices();
		resolveInterfaceDependices();
		resolveAgentsDependices();
		//resolveProcessDependices();
		resolveServiceDependices();
	}
	
	private void resolveAgentsDependices() {
		//resolve "Enterprise Service" agents from Interfaces sheet and add 'em to Agents list - RSingh 5/19/2010.
		HashMap<String, String> enterpriseServiceAgents = new HashMap<String, String>();
		for (Interface i : intList) {
			if(Utils.isEmpty(i.candUseOfEntServ))
				continue;
			String candUseOfEntServ = i.candUseOfEntServ.trim();
			String[] tokens = candUseOfEntServ.split("[,;]");
			for(String tok : tokens) {
				tok = tok.trim();
				if("N/A".equalsIgnoreCase(tok) || "TBD".equalsIgnoreCase(tok) || 
						"New".equalsIgnoreCase(tok) || "New **".equalsIgnoreCase(tok)) {
					loger.warn("Ignore to add 'Enterprise Service' Agent [" + tok + "] derived from Interface [" + i.name + "]");
					continue;
				}
				tok = tok.replaceAll("[*]", "");
				tok = tok.trim();
				String rel = enterpriseServiceAgents.get(tok);
				if(Utils.isEmpty(rel)) {
					rel = i.release;
				} else {
					if(rel.indexOf(i.release) == -1)
						rel += ("|" + i.release);
				}
				enterpriseServiceAgents.put(tok, rel);
			}
		}
		Set<String> keys = enterpriseServiceAgents.keySet();
		for(String key : keys) {
			Agent newEntAgent = new Agent();
			newEntAgent.id = Utils.getNextId();
			newEntAgent.stereoType = Agent.ENT_SERV_STEREO_TYPE;
			newEntAgent.name =  key;
			newEntAgent.owner = "ESB";
			newEntAgent.release = enterpriseServiceAgents.get(key);
			this.agentList.add(newEntAgent);
		}

		List<RelatedDataReference> inList = null;
		for (Agent a : agentList) {
			if(!Utils.isEmpty(a.dataInit)) {
				inList = Utils.createNewRefDataClasses(this, a.dataInit, DataDirType.I, a);
				this.addRelatedDataReference(inList);
			}
			if(!Utils.isEmpty(a.dataResp)) {
				inList = Utils.createNewRefDataClasses(this, a.dataResp, DataDirType.O, a);
				this.addRelatedDataReference(inList);
			}

			if(!Utils.isEmpty(a.connExtApp)) {
				String[] appStrList = a.connExtApp.split("[,]");
				for (String appName : appStrList) {
					Application appObj = Utils.findApplicationByName(appName, appList);
					if(appObj == null) 
						loger.error("Unable to find <" + appName + "> for <" + a.name + "> of type [" + a.stereoType + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(a, appObj, null));					
				}
			}
			if(!Utils.isEmpty(a.connExtComp)) {
				String stType = Agent.ENT_SERV_STEREO_TYPE;
				String[] agList = a.connExtComp.split("[,]");
				for (String ag : agList) {
					Agent agent = Utils.findAgentByName(null, ag, agentList);
					if(agent == null) 
						loger.error("Unable to find <" + ag + "> of type [" + stType + "] for <" + a.name + "> of type [" + a.stereoType + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(a, agent, null));					
				}
			}
			if(!Utils.isEmpty(a.placement)) {
				String stICType = "Integration Config";
				String stHSType = "Hosting Config";
				String[] actList = a.placement.split("[,]");
				for (String uc : actList) {
					IntegrationComp intComp = Utils.findIntegrationUCByName(stICType, uc, intCompList);
					if(intComp != null) { 
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(a, intComp, null));
						continue;
					}
					intComp = Utils.findIntegrationUCByName(stHSType, uc, intCompList);
					if(intComp == null) 
						loger.error("Unable to find <" + uc + "> of type [" + stICType + " or " + stHSType + "] for <" + a.name + "> of type [" + a.stereoType + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(a, intComp, null));					
				}
			}
		}
	}
	
	private void resolveInterfaceDependices() {
		List<RelatedDataReference> inList = null;
		for (Interface i : intList) {
			if(!Utils.isEmpty(i.ioeagent)) {
				String stType = "IOE Enterprise Agent";
				String[] agList = i.ioeagent.split("[,]");
				for (String ag : agList) {
					Agent agent = Utils.findAgentByName(stType, ag, agentList);
					if(agent == null) 
						loger.error("Unable to find <" + ag + "> of type [" + stType + "] for <" + i.name + "> of type [" + i.stereotype + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(i, agent, null));					
				}
			}
			if(!Utils.isEmpty(i.actorusing)) {
				String[] actList = i.actorusing.split("[,]");
				for (String a : actList) {
					Actor actor = Utils.findActorByName(a, actorList);
					if(actor == null) 
						loger.error("Unable to find <" + a + "> of type [" + EntityType.ACTR + "] for <" + i.name + "> of type [" + i.stereotype + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(i, actor, null));					
				}
			}
			//added on 07/19/2010 - RSingh
			if(!Utils.isEmpty(i.usecase)) {
				String[] usecaseList = i.usecase.split("[,]");
				for (String uc : usecaseList) {
					UseCase u = Utils.findUseCaseByNumber(uc.trim(), useCaseList);
					if(u == null) {
						loger.error("Unable to locate UseCase Service Defination [" + uc + "] for Interface [" + i.name + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(i, u, null));
				}
			}
			if(!Utils.isEmpty(i.integHosting)) {
				String stType = "Hosting Config";
				String[] actList = i.integHosting.split("[,]");
				for (String a : actList) {
					IntegrationComp intComp = Utils.findIntegrationUCByName(stType, a, intCompList);
					if(intComp == null) 
						loger.error("Unable to find <" + a + "> of type [" + stType + "] for <" + i.name + "> of type [" + i.stereotype + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(i, intComp, null));					
				}
			}
			//TODO: PSD also has data part for interfaces, need to finalize what is right - RS 2/9/10
			if(!Utils.isEmpty(i.reqdataflow)) {
				inList = Utils.createNewRefDataClasses(this, i.reqdataflow, DataDirType.I, i);
				this.addRelatedDataReference(inList);
			}
			if(!Utils.isEmpty(i.respdataflow)) {
				inList = Utils.createNewRefDataClasses(this, i.respdataflow, DataDirType.O, i);
				this.addRelatedDataReference(inList);
			}
			//moved from UML Creator to create Interface dependency with in Service definition 03/23/2010.
			String dataList = i.serviceref;
			if(!Utils.isEmpty(dataList)) {
				String[] otherServList = dataList.split("[,]");
				for(String serv : otherServList) {
					Service oServ = Utils.findServiceByName(serv, this.servList);
					if(oServ == null) {
						loger.error("Unable to locate other service [" + serv + "] for Interface [" + i.name + "]");
						continue;
					}
					//here source & target entities are reversed because the relation is existed with target (INTR) entity - RSingh 03/23/2010
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(oServ, i, null));					
				}
			}
		}
	}

	private void resolveApplicationDependices() {
		for (Application app : appList) {
			if(Utils.isEmpty(app.hostConfig))
				continue;
			String[] hostCnfg = app.hostConfig.split("[,]");
			for (String hc : hostCnfg) {
				hc = hc.trim();
				IntegrationComp intComp = Utils.findIntegrationUCByName(IntegrationComp.STEREO_TYPE, hc, intCompList);
				if(intComp == null) 
					loger.error("Unable to find <" + hc + "> of type [" + IntegrationComp.STEREO_TYPE + "] for <" + app.name + "> of type [" + Application.STEREO_TYPE + "]");				
				else
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(app, intComp, null));					
			}
		}
	}
	private void resolveIntegrationUCDependices() {
		for (IntegrationComp ic : intCompList) {
			if(!Utils.isEmpty(ic.intSWPkg)) {
				String[] swPkgs = ic.intSWPkg.split("[,]");
				for (String sw : swPkgs) {
					sw = sw.trim();
					IntegrationSW intSW = Utils.findIntegrationSWByName(sw, intSWList);
					if(intSW == null) {
						loger.error("Unable to find <" + sw + "> of type [" + IntegrationSW.STEREO_TYPE + "] for <" + ic.name + "> of type [" + ic.stereoType + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(ic, intSW, IntegrationSW.INT_TYPE));					
				}
			}
			if(!Utils.isEmpty(ic.buildSW)) {
				String[] swPkgs = ic.buildSW.split("[,]");
				for (String sw : swPkgs) {
					sw = sw.trim();
					IntegrationSW intSW = Utils.findIntegrationSWByName(sw, intSWList);
					if(intSW == null) {
						loger.error("Unable to find <" + sw + "> of type [" + IntegrationSW.STEREO_TYPE + "] for <" + ic.name + "> of type [" + ic.stereoType + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(ic, intSW, IntegrationSW.BUILD_TYPE));					
				}				
			}
			if(!Utils.isEmpty(ic.security) && Constants.bResolveSecConfig) {
				String[] secToks = ic.security.split("[,]");
				for (String sec : secToks) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + ic.name + "> of type [" + ic.stereoType + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(ic, secComp, SecurityComp.INT_TYPE));					
				}				
			}
			if(!Utils.isEmpty(ic.securityConfig) && Constants.bResolveSecConfig) {
				String[] secToks = ic.securityConfig.split("[,]");
				for (String sec : secToks) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + ic.name + "> of type [" + ic.stereoType + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(ic, secComp, SecurityComp.USED_TYPE));					
				}				
			}
		}
		
	}
	private void resolveInfrastructureDependices() {
		for (InfrastructureLS iLS : infLSList) {
			if(!Utils.isEmpty(iLS.intHostConfig)) {
				String[] lsPkgs = iLS.intHostConfig.split("[,]");
				for (String ls : lsPkgs) {
					ls = ls.trim();
					IntegrationComp intComp = Utils.findIntegrationUCByName(null, ls, intCompList);
					if(intComp == null) {
						loger.error("Unable to find <" + ls + "> of type [" + IntegrationComp.STEREO_TYPE + "] for <" + iLS.name + "> of type [" + InfrastructureLS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iLS, intComp, null));					
				}
			}
			if(!Utils.isEmpty(iLS.supSecCap) && Constants.bResolveSecConfig) {
				String[] secPkgs = iLS.supSecCap.split("[,]");
				for (String sec : secPkgs) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + iLS.name + "> of type [" + InfrastructureLS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iLS, secComp, SecurityComp.SUP_TYPE));					
				}				
			}
			if(!Utils.isEmpty(iLS.intSecCap) && Constants.bResolveSecConfig) {
				String[] secPkgs = iLS.intSecCap.split("[,]");
				for (String sec : secPkgs) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + iLS.name + "> of type [" + InfrastructureLS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iLS, secComp, SecurityComp.INT_TYPE));					
				}				
			}
		}		
		for (InfrastructurePS iPS : infPSList) {
			if(!Utils.isEmpty(iPS.infLSImage)) {
				String[] lsPkgs = iPS.infLSImage.split("[,]");
				for (String ls : lsPkgs) {
					ls = ls.trim();
					InfrastructureLS infLS = Utils.findInfrastructureLSByName(ls, infLSList);
					if(infLS == null) {
						loger.error("Unable to find <" + ls + "> of type [" + InfrastructureLS.STEREO_TYPE + "] for <" + iPS.name + "> of type [" + InfrastructurePS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iPS, infLS, null));					
				}
			}
			if(!Utils.isEmpty(iPS.supSecCap) && Constants.bResolveSecConfig) {
				String[] secPkgs = iPS.supSecCap.split("[,]");
				for (String sec : secPkgs) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + iPS.name + "> of type [" + InfrastructurePS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iPS, secComp, SecurityComp.SUP_TYPE));					
				}				
			}
			if(!Utils.isEmpty(iPS.usedSecCap) && Constants.bResolveSecConfig) {
				String[] secPkgs = iPS.usedSecCap.split("[,]");
				for (String sec : secPkgs) {
					sec = sec.trim();
					SecurityComp secComp = Utils.findSecurityCompByName(sec, secCompList);
					if(secComp == null) {
						loger.error("Unable to find <" + sec + "> of type [" + SecurityComp.STEREO_TYPE + "] for <" + iPS.name + "> of type [" + InfrastructurePS.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iPS, secComp, SecurityComp.USED_TYPE));					
				}				
			}
		}		
		for (InfrastructureLSInst iLSInst : infLSInstList) {
			if(!Utils.isEmpty(iLSInst.psServer)) {
				String[] lsPkgs = iLSInst.psServer.split("[,]");
				for (String ps : lsPkgs) {
					ps = ps.trim();
					InfrastructurePS intComp = Utils.findInfrastructurePSByName(ps, infPSList);
					if(intComp == null) {
						loger.error("Unable to find <" + ps + "> of type [" + InfrastructurePS.STEREO_TYPE + "] for <" + iLSInst.name + "> of type [" + InfrastructureLSInst.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iLSInst, intComp, null));					
				}
			}
			if(!Utils.isEmpty(iLSInst.lsImage)) {
				String[] lsPkgs = iLSInst.lsImage.split("[,]");
				for (String ls : lsPkgs) {
					ls = ls.trim();
					InfrastructureLS intComp = Utils.findInfrastructureLSByName(ls, infLSList);
					if(intComp == null) {
						loger.error("Unable to find <" + ls + "> of type [" + InfrastructureLS.STEREO_TYPE + "] for <" + iLSInst.name + "> of type [" + InfrastructureLSInst.STEREO_TYPE + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(iLSInst, intComp, null));					
				}
			}
		}
	}
	
	private void resolveProcessDependices() {
		for (Processes p : procList) {
			if(!Utils.isEmpty(p.busProcName)) {
				String[] bpStrList = p.busProcName.split("[\n]");
				for (String bpS : bpStrList) {
					bpS = bpS.trim();
					BusinessProcess bpComp = Utils.findBusinessProcessByName(bpS, bProcessList);
					if(bpComp == null) 
						loger.error("Unable to find <" + bpS + "> of type [" + EntityType.BPROC + "] for <" + p.name + "> of type [" + p.stereotype + "]");				
					else
						addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(p, bpComp, null));					
				}
			}
		}		
	}
	
	private void resolveServiceDependices() {
		List<RelatedDataReference> inList = null;
		List<RelatedDataReference> servInList = null;
		List<RelatedDataReference> servOutList = null;
		
		String dataList = null;
		for (Service s : servList) {
			dataList = s.otherservices;
			if(!Utils.isEmpty(dataList)) {
				String[] otherServList = dataList.split("[,]");
				for(String serv : otherServList) {
					Service oServ = Utils.findServiceByName(serv, this.servList);
					if(oServ == null) {
						loger.error("Unable to locate other service [" + serv + "] for Service [" + s.name + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(s, oServ, null));
				}
			}
			dataList = s.servDefID;
			if(!Utils.isEmpty(dataList)) {
				String[] otherUCList = dataList.split("[,]");
				for(String uc : otherUCList) {
					UseCase u = Utils.findUseCaseByNumber(uc, this.useCaseList);
					if(u == null) {
						loger.error("Unable to locate UseCase Service Defination [" + uc + "] for Service [" + s.name + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(s, u, null));
				}
			}
			dataList = s.agent;
			if(!Utils.isEmpty(dataList)) {
				String[] otherAgentList = dataList.split("[,]");
				for(String agnt : otherAgentList) {
					Agent a = Utils.findAgentByName(null, agnt, this.agentList);
					if(a == null) {
						loger.error("Unable to locate Agent [" + agnt + "] for Service [" + s.name + "]");
						continue;
					}
					addEntitiesReferences(EntitiesReferences.getNewEntitiesReference(s, a, null));
				}
			}
			
			if(s.opList == null || s.opList.size() == 0)
				continue;
			for (ServiceOperation sop : s.opList) {
				if(!Utils.isEmpty(sop.dataIn)) {
					inList = Utils.createNewRefDataClasses(this, sop.dataIn, DataDirType.I, sop);
					this.addRelatedDataReference(inList);
					//now add all the operation "in" data items to Service level list (accumulation) - RSingh 04/01/2010
					inList = Utils.createNewRefDataClasses(this, sop.dataIn, DataDirType.I, s);
					servInList = appendRelatedDataReference2GivenList(servInList, inList);
				}
				if(!Utils.isEmpty(sop.dataOut)) {
					inList = Utils.createNewRefDataClasses(this, sop.dataOut, DataDirType.O, sop);
					this.addRelatedDataReference(inList);
					//now add all the operation "out" data items to Service level list (accumulation) - RSingh 04/01/2010
					inList = Utils.createNewRefDataClasses(this, sop.dataOut, DataDirType.O, s);
					servOutList = appendRelatedDataReference2GivenList(servOutList, inList);
				}
			}
			//now add all the Data Objects identified at Service level, list them in SOAData - RSingh 04/01/2010
			if(servInList != null && servInList.size() > 0)
				this.addRelatedDataReference(servInList);
			if(servOutList != null && servOutList.size() > 0)
				this.addRelatedDataReference(servOutList);
		}
	}
}
